package com.xiaoze.freqencyControl.frequencycontrol.strategy;


import com.xiaoze.freqencyControl.constant.FrequencyControlConstant;
import com.xiaoze.freqencyControl.domain.dto.SlidingWindowDTO;
import com.xiaoze.freqencyControl.frequencycontrol.AbstractFrequencyControlService;
import com.xiaoze.freqencyControl.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;


/**
 * 抽象类频控服务 -使用redis实现 滑动窗口是一种更加灵活的频率控制策略，它在一个滑动的时间窗口内限制操作的发生次数
 */
@Slf4j
@Service
public class SlidingWindowFrequencyController extends AbstractFrequencyControlService<SlidingWindowDTO> {
    @Override
    protected boolean reachRateLimit(Map<String, SlidingWindowDTO> frequencyControlMap) {
        // 批量获取redis统计的值
        // 将frequencyControlMap中的所有key转换为一个List
        List<String> frequencyKeys = new ArrayList<>(frequencyControlMap.keySet());
        // 遍历所有的key
        for (int i = 0; i < frequencyKeys.size(); i++) {
            // 获取当前key
            String key = frequencyKeys.get(i);
            // 根据key从frequencyControlMap中获取对应的SlidingWindowDTO对象
            SlidingWindowDTO controlDTO = frequencyControlMap.get(key);
            // 调用RedisUtils工具类的ZSetGet方法，获取指定key在滑动窗口时间内的计数值
            Long count = RedisUtils.ZSetGet(key);
            // 获取controlDTO中设定的频率限制计数值
            int frequencyControlCount = controlDTO.getCount();
            // 检查计数值是否非空并且是否超过了设定的频率限制
            if (Objects.nonNull(count) && count >= frequencyControlCount) {
                // 如果频率超过了限制，输出警告日志并返回true
                log.warn("frequencyControl limit key:{},count:{}", key, count);
                return true;
            }
        }
        // 如果所有key的计数值都没有超过频率限制，返回false
        return false;

    }

    @Override
    protected void addFrequencyControlStatisticsCount(Map<String, SlidingWindowDTO> frequencyControlMap) {
        // 将frequencyControlMap中的所有key转换为一个List
        List<String> frequencyKeys = new ArrayList<>(frequencyControlMap.keySet());
        // 遍历所有的key
        for (int i = 0; i < frequencyKeys.size(); i++) {
            // 获取当前key
            String key = frequencyKeys.get(i);
            // 根据key从frequencyControlMap中获取对应的SlidingWindowDTO对象
            SlidingWindowDTO controlDTO = frequencyControlMap.get(key);
            // 将窗口的最小周期转换为毫秒
            long period = controlDTO.getUnit().toMillis(controlDTO.getPeriod());
            // 获取当前时间的毫秒值
            long current = System.currentTimeMillis();
            // 计算窗口的总大小（单位为毫秒）
            long length = period * controlDTO.getWindowSize();
            // 计算窗口的起始时间（毫秒值）
            long start = current - length;
            // 调用RedisUtils的ZSetAddAndExpire方法，将当前时间添加到有序集合中，并设置过期时间
            // start参数用于删除窗口之前的数据
            // length参数用于设置过期时间窗口的长度
            RedisUtils.ZSetAddAndExpire(key, start, length, current);
        }

    }

    @Override
    protected String getStrategyName() {
        return FrequencyControlConstant.SLIDING_WINDOW;

    }
}
